package cn.wdm.ymcc.service.impl;

import cn.wdm.ymcc.domain.CourseType;
import cn.wdm.ymcc.dto.MbxDto;
import cn.wdm.ymcc.mapper.CourseTypeMapper;
import cn.wdm.ymcc.result.JSONResult;
import cn.wdm.ymcc.service.ICourseTypeService;
import com.alibaba.csp.sentinel.util.AssertUtil;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;

/**
 * <p>
 * 课程目录 服务实现类
 * </p>
 *
 * @author lao.zhang
 * @since 2022-07-20
 */
@Service
@Slf4j
public class CourseTypeServiceImpl extends ServiceImpl<CourseTypeMapper, CourseType> implements ICourseTypeService {
    @Autowired
    private CourseTypeMapper courseTypeMapper;
    @Autowired
    private RedisTemplate<Object,Object> redisTemplate;

    @Override
    @Cacheable(cacheNames = "coursetype",key = "'tree'")// 将当前方法的返回值，以coursetype::tree为redis中的key，存储在redis中。
    //数据调用该方法，先查询redis中的数据，如果没有才执行该方法返回数据。
    public List<CourseType> getCourseTypeTree() {
        log.info("我走了mysql去获取数据返回");
        return getCourseTypeTreeMethod();
    }
    @Override
    public JSONResult getCourseTypeCrums(Long courseTypeId) {
        /**
         * 1.入参校验
         * 2.根据当前课程分类ID查询课程分类详情
         * 3.得到id的path，分割path得到多个id的集合
         * 4.根据集合批量查询出多个分类对象
         * 5.循环分类，每次循环需要创建一个mbxDto，把当前分类装到ownerCourseType
         * 6.为当前分类查询同级分类列表，装到mbxDto的otherCourseTypes
         */
        //1.入参校验 @TODO
        //2.根据当前课程分类ID查询课程分类详情
        List<MbxDto> returnList = new ArrayList<>();
        CourseType courseType = courseTypeMapper.selectById(courseTypeId);
        //3.得到id的path，分割path得到多个id的集合
        String[] split = courseType.getPath().split("\\.");
        //4.根据集合批量查询出多个分类对象
        List<CourseType> courseTypes = courseTypeMapper.selectBatchIds(Arrays.asList(split));
        //5.循环分类，每次循环需要创建一个mbxDto，把当前分类装到ownerCourseType
        for (CourseType coursetype:courseTypes
        ) {
            MbxDto mbxDto = new MbxDto();
            mbxDto.setOwnCourseType(coursetype);
            //6.为当前分类查询同级分类列表，装到mbxDto的otherCourseTypes
            //根据pid查询 下面三行代码相当于手写sql：selset * from course_type where pid=? and id !=?
            Wrapper<CourseType> wrapper = new EntityWrapper<>();
            wrapper.eq("pid",coursetype.getPid())
                    .ne("id",coursetype.getId());
            List<CourseType> otherCourseTppes = courseTypeMapper.selectList(wrapper);

            mbxDto.setOtherCourseTppes(otherCourseTppes);
            returnList.add(mbxDto);
        }
        return JSONResult.success(returnList);
    }

    /**
     * 没有引用springCatch分布式缓存的写法
     *  将查询到的无限级课程类型树存入到redis
     *  1.查询原来redis中可能存在的数据
     *  2.有 直接返回数据
     *  3.没有 就查询出数据返回，然后将查询出来的数据存redis
     * @return
     */
    public List<CourseType> getCourseTypeTreeBefore() {
        //存储无限级树在redis中的key
        String courseTypeTreeKey = "coursetypetree";
        //从redis中获取可能存在的无限级树
        Object courseTypeTreeObj = redisTemplate.opsForValue().get(courseTypeTreeKey);
        if (courseTypeTreeObj != null) {
            return (List<CourseType>) courseTypeTreeObj;
        }else{
            List<CourseType> courseTypeTrees = getCourseTypeTreeMethod();
            redisTemplate.opsForValue().set(courseTypeTreeKey,getCourseTypeTreeMethod());
            return courseTypeTrees;
        }
    }
    //为了mysql和redis的数据尽可能的一致，所以在对数据库的增删改后，应该对redis中的数据进行一个剔除。
    //1.增后 剔除redis的数据
    @Override
    @CacheEvict(cacheNames = "coursetype",key = "'tree'")
    public boolean insert(CourseType entity) {
        return super.insert(entity);
    }
    /*@Override 原始的写法，没有集成springcatch
    public boolean insertBeforeTest(CourseType entity) {
        //新增mysql的数据
        boolean boo = insert(entity);
        //剔除redis的数据
        //存储无限级树在redis中的key
        String courseTypeTreeKey = "coursetypetree";
        redisTemplate.delete(courseTypeTreeKey);
        return boo;
    }*/
    //2.删后 剔除redis的数据
    @Override
    @CacheEvict(cacheNames = "coursetype",key = "'tree'")
    public boolean deleteById(Serializable id) {
        return super.deleteById(id);
    }
    //1.改后 剔除redis的数据
    @Override
    @CacheEvict(cacheNames = "coursetype",key = "'tree'")
    public boolean updateById(CourseType entity) {
        return super.updateById(entity);
    }
    /**
     * 抽取方法：获取课程类型的无限级树
     * @return
     */
    public List<CourseType> getCourseTypeTreeMethod() {
        //1.查询全部
        List<CourseType> courseTypes = courseTypeMapper.selectList(null);

        //2.建立map集合，将所有的数据转为一个map集合
        HashMap<Long, CourseType> treeMap = new HashMap<Long, CourseType>();
        //将每次遍历的值给map集合，key是id，valule是自己
        for (CourseType courseType : courseTypes) {
            treeMap.put(courseType.getId(),courseType);
        }
        //3.查找自己的祖宗
        ArrayList<CourseType> firstCourseTypes = new ArrayList<>();
        //遍历查找自己上一级，如果没有上一级就是祖宗，将其放入到指定的集合里面
        //如果有上一级，就将自己放到，爹的courseType的children集合里面
        for (CourseType courseType : courseTypes) {
            //当前对象没有上级的id，就是祖宗。
            if (courseType.getPid().intValue()==0){
                firstCourseTypes.add(courseType);
            }else{
                //有上级的id，将上级查到，将自己放到上级部门的children中
                //这是上级 精准的从map集合中获取到自己的上级
                CourseType parentCourseType = treeMap.get(courseType.getPid());
                //将自己放到上级部门的children中
                if(parentCourseType!=null){
                    parentCourseType.getChildren().add(courseType);
                }
            }
        }
        return firstCourseTypes;
    }

}
